#pragma once

#include "sock.hpp"

#include <sys/select.h>
#include <sys/types.h>

namespace ns_select
{
    //文件描述符最多被接收的数量NUM
    #define NUM (sizeof(fd_set) * 8)

    using namespace ns_sock;
    const uint16_t g_default = 8080;

    class SelectServer
    {
    public:
        SelectServer(u_int16_t port = g_default) : port_(port), listen_sock_(-1)
        {
            //第三方位图数组初始化
            for(int i = 0; i < NUM; i++)
            {
                fd_arrar_[i] = -1;
            }
        }

        void InitSelectServer()
        {
            listen_sock_ = Sock::Socket();
            Sock::Bind(listen_sock_, port_);
            Sock::Listen(listen_sock_);
            //将listen_sock保存到数组
            fd_arrar_[0] = listen_sock_;
        }

        std::ostream& PrintFd()
	    {
            for(int i = 0; i < NUM; i++)
            {
                if(fd_arrar_[i] != -1) std::cout << fd_arrar_[i] << ' ';
            }
            return std::cout;
        }

        //对新链接的到来 或者 新数据 的处理
        void HandlerEvent(const fd_set &rfds)
        {
            //判断我的有效sock，是否在rfds中
            for(int i = 0; i < NUM; i++)
            {
                if(fd_arrar_[i] == -1)
                    continue;
                //如何区分: 新链接到来，真正的数据到来？
                if(FD_ISSET(fd_arrar_[i], &rfds))
                {
                    if(fd_arrar_[i] == listen_sock_)
                    {
                        //新链接到来
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int sock = accept(listen_sock_, (struct sockaddr*)&peer, &len);
                        if(sock < 0)
                        {
                            std::cout << "accept error" << std::endl;
                        }
                        else 
                        {
                            //不能直接读取新的sock，为什么要将它添加到数组就完了？？
                            //将新的sock添加到文件描述符数组中！
                            int j = 0;
                            for(; j < NUM; j++)
                            {
                                if(fd_arrar_[j] == -1)
                                    break;
                            }
                            if(j == NUM)
                            {
                                std::cout << "fd_array 已经满了!" << std::endl; 
                                close(sock);
                            }
                            else 
                            {
                                fd_arrar_[j] = sock;
                                std::cout << "获取新的链接成功, sock: " << sock << " 已经添加到数组中了, 当前: " << std::endl;
                                PrintFd() << " [当前]" << std::endl;                                
                            }
                        }
                    }
                    else 
                    {
                        // 数据到来
                        // 这样写是有BUG的！这里不解决，epoll
                        // 你能保证你用1024就能读取完毕吗？？有没有可能有粘包问题？？
                        // 网络通信，定制协议，业务场景有关
                        // 是不是每一个sock，都必须有自己独立的buffer
                        char buffer[1024];
                        ssize_t s = recv(fd_arrar_[i], buffer, sizeof(buffer), 0);
                        if( s > 0 )
                        {
                            buffer[s] = '\0';
                            std::cout << "clint say# " << buffer << std::endl;
                        }
                        else if(s == 0)
                        {
                            std::cout << "client quit ---- sock: " << fd_arrar_[i] << std::endl;

                            // 对端链接关闭
                            close(fd_arrar_[i]);

                            // 从rfds中，去掉该sock
                            fd_arrar_[i] = -1;

                            PrintFd() << " [当前]" << std::endl;

                        }
                        else
                        {
                            //读取异常，TODO
                            std::cerr << "recv error" << std::endl;
                        }
                    }
                }
            }
        }

        void Loop()
        {
            //这样写有问题吗

            //在服务器最开始的时候，我们只有一个sock，listen_sock, 有读事件就绪，读文件描述符看待的！
            fd_set rfds; // 3, 4,5,6
            while(true)
            {
                FD_ZERO(&rfds); // 对位图结构进行清空
                // FD_SET(listen_sock_, &rfds);
                struct timeval timeout = {3, 0};    //select最后一个参数所用等待时间，不要就是阻塞等待
                int max_fd = -1;    //可方便计算select的第一个参数
                for(int i = 0; i < NUM; i++)
                {
                    if(fd_arrar_[i] == -1)
                        continue;
                    FD_SET(fd_arrar_[i], &rfds);    //将就绪的文件描述符放入rfds
                    if(max_fd < fd_arrar_[i])
                        max_fd = fd_arrar_[i];
                }

                int n = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout);
                switch (n)
                {
                case 0:
                    std::cout << "timeout ..." << std::endl;
                    break;
                case -1:
                    std::cout << "select error" << std::endl;
                    break;
                default:
                    // select成功, 至少有一个fd是就绪的
                    HandlerEvent(rfds);
                    // std::cout << "有事件发生了..." << std::endl;
                    break;
                }
            }
        }

        ~SelectServer()
        {
            if (listen_sock_ >= 0)
                close(listen_sock_);
        }

    private:
        u_int16_t port_;
        int listen_sock_;
        int fd_arrar_[NUM]; //第三方 用于临时存储 位图结构的数组
        
        // select是可以等待多个fd的，listen_sock_只是其中之一
        // 如果有新的链接到来，一定对应的是有新的sock，你如何保证新的sock也被添加到select 中？
        // rfds: 1111 1111 (输入)
        //       1000 0000 (输出)
        // select 要被使用，需要借助于一个第三方数组，管理所有的有效sock
        // fd_set: 不要把它当做具有sock保存的功能，它只有互相通知(内核<->用户)的能力
        // select : 通知sock就绪之后，上层读取，可能还需要继续让select帮我们进行检测，对rfds进行重复设置
    };
}